package gbench.sandbox.matlib;

import org.junit.jupiter.api.Test;

import java.text.NumberFormat;
import java.util.Arrays;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import gbench.common.matlib.rdd.PVec;
import gbench.common.tree.LittleTree;
import gbench.common.tree.LittleTree.Elem;
import gbench.common.tree.LittleTree.IRecord;
import gbench.common.tree.LittleTree.KVPair;
import gbench.common.matlib.MatlibCanvas;
import gbench.common.matlib.MatlibCanvas.Seq;
import gbench.common.matlib.MatlibCanvas.NVec;
import gbench.common.matlib.MatlibCanvas.PatternCase;
import gbench.common.matlib.MatlibCanvas.Range;
import gbench.common.matlib.MatlibCanvas.Weightable;
import gbench.common.matlib.MatlibCanvas.XVec;

import static java.lang.Math.PI;
import static gbench.common.matlib.data.Pipe.PIPE;
import static gbench.common.matlib.MatlibCanvas.Range.P2;
import static gbench.common.matlib.MatlibCanvas.Range.RG;
import static gbench.common.matlib.MatlibCanvas.Range.Rg;
import static gbench.common.matlib.MatlibCanvas.Range.NP;
import static gbench.common.matlib.MatlibCanvas.Range.NP2;
import static gbench.common.matlib.MatlibCanvas.Range.aaclc;
import static gbench.common.matlib.MatlibCanvas.Range.rG;
import static gbench.common.matlib.MatlibCanvas.Range.rg;
import static gbench.common.tree.LittleTree.compose_f;
import static gbench.common.tree.LittleTree.IRecord.*;
import static gbench.common.matlib.MatlibCanvas.println;
import static gbench.common.matlib.MatlibCanvas.withT;
import static gbench.common.matlib.MatlibCanvas.withTU;
import static gbench.common.matlib.MatlibCanvas.PatternCase.defaultcase;

/**
 * NVec 演示
 * @author gbench
 *
 */
public class JunitNVec {
    
    @Test
    public void foo() {
        
        final var rng = Range.of2(1,10);
        final var nvec = NVec.of(rng);
        final var nvec1 = NVec.of(rng.reverse()); // 由于rng 是包含状态所以他的start是11而不是10
        println("nvec:"+nvec);
        println("nvec 有stream构造:"+NVec.of(Stream.iterate(0, i->i+1).limit(50)));
        println("nvec1(由于rng 是包含状态所以他的start是11而不是10):"+nvec1);
        println("add(_1,_1):"+nvec.add(nvec));
        println("sub(_1,_1):"+nvec.sub(nvec));
        println("sub2(_1,_1*3):"+nvec.sub2(nvec.mul(NVec.of(3))));
        println("mul(_1,_1):"+nvec.mul(nvec));
        println("div(_1,_1):"+nvec.div(nvec));
        println("div2(_1*2,_1):"+nvec.mul(NVec.of(2)).div2(nvec));
        println("div(_1,_1*2)  循环补位:"+nvec.div(nvec.mul(2)));
        
        println("var:"+nvec.var());
        println("sd:"+nvec.sd());
        println("sum:"+nvec.sum());
        println("dot:"+nvec.dot(nvec));
        println("dot:"+nvec.add(nvec));
        println("product:"+nvec.product());
        println("matrix:\n"+nvec.matrix());
        println("matrix.transpose:\n"+nvec.matrix(e->e.transpose()));
    }
    
    /**
     * 向量的列顺序编排成矩阵
     */
    @Test
    public void flatten() {
        
        final var vec = NVec.of(Range.of(81));
        final var oo = vec.reshape(3,3,3,3);
        final var xvec = XVec.OF(oo);
        
        println("reshape后的数据"+Arrays.deepToString(oo));
        println("没有flat的向量结果:"+xvec);
        println("flat 扁平化的结果 :\n"+XVec.flat(oo,Double.class));
        
        println("索引2,2,2,2的值:"+Seq.read(oo,2,2,2,2));
        Seq.write(oo, 100d, 2,2,2,2);
        println("修改索引2,2,2,2的值为100");
        XVec.flattenS(oo,Double.class).forEach(ivp->{
            println(ivp);
        });
        
        println("遍历一个普通对象,index为空数组");
        XVec.flattenS(3,Integer.class).forEach(ivp->{
            println(ivp);
        });
    }
    
    @Test
    public void permute() {
        
        Range.cph(Range.of2(1, 2), Range.of2(3, 4)).forEach(e -> {
            println(e);
        });
        
        println();
        final var rng = Range.of(10);
        Range.cphx(e->{
            println(e);
        },rng,rng,rng);
    }
    
    /**
     * Permutation cph 性能
     */
    @Test
    public void range_permute() {
        
        final var p = new Range.Permutation<String>(ranges);
        final var last = LittleTree.timeit(()->p.permute(Arrays::deepToString, e -> {
            //println(e);
        }));
        System.err.println("Range.Permutation:10^"+ranges.length+"量级 lasts for "+last/1000+" ms");
    }

    /**
     * 千万数据 Elem.cph:7 lasts for 0.29846429999999996 ms
     */
    @Test
    public void elem_cph() {
        
        final var last = LittleTree.timeit(()->Elem.cph(ranges).forEach(e -> {
            //println(e);
        }));
        System.err.println("Elem.cph:10^"+ranges.length+"量级 lasts for "+last/1000+" ms");
    }
    
    /**
     * 位图模式的全排列:8进制转换器的一种实现。
     */
    @Test
    public void shift_cph() {
        
        // 0到8的全排序,注意由于是采用整数做存储空间 方便使用移位操作,所以n*width 不能超过32
        final var n = 3; // 2的3次幂是8,元素的长度，即元素的种类。比如 8个元素，就是2的3次幂。
        final var width = 4; // 排序规模/宽度,每次排列的元素个数,即每个排列中有几个元素,排几个元素
        
        // 采用掩码来做数据内容分解,掩码是一种分解方法
        final var mask = (1 << n) - 1; // 数据掩码 用于提取模式内容来生成元素
        rg(1 << (n * width)).map(Number::intValue).forEach(e -> {
            final var line = rg(0, width).map(i -> i * n).map(Number::intValue) // 生成右移长度
                .map(x -> (e & (mask << x)) >> x) // 提取掩码所标记的数据
                .collect(MatlibCanvas.NVec.nveclc()).reverse(); // 倒序
            println(e, line.intA());
        });
    }
    
    /**
     * 千万数据直接奔溃
     */
    @Test
    public void littletree_cph() {
        
        final var last = LittleTree.timeit(()->LittleTree.cph((Object[][])ranges).forEach(e -> {
            //println(e);
        }));
        System.err.println("LittleTree.cph:10^"+ranges.length+"量级 lasts for "+last/1000+" ms");
    }

    @Test
    public void total() {
        
        println("全排序的性能");
        this.range_permute();
        this.elem_cph();
        this.littletree_cph();
    }
    
    @Test
    public void groupby() {
        
        final var nvec = NVec.of(Range.of(100));
        final var rec = REC(nvec.groupBy(e->e%2));
        println(rec.toString2());
        nvec.groupBy(e->e%2,Collectors.summarizingDouble(e->e),IRecord::REC).foreach((k,v)->{
            println(k+"---->"+v);
        });
    }
    
    /**
     * 扔两个色子点数乘积的可能情况分布
     */
    @Test
    public void probability() {
        
        Range.cph(Range.of2(1,6),Range.of2(1,6)).collect(Collectors.groupingBy(e->e.product()))
        .entrySet().stream().sorted((a,b)->a.getKey().intValue()-b.getKey().intValue())
        .forEach(e->{
            e.getValue().forEach(o->Arrays.sort(o.data()));// 排序
            final var mm = e.getValue().stream().collect(Collectors.groupingBy(w->w));//分组
            println(e.getKey()+"->"+mm);
        });
    }
    
    @Test
    public void reshape() {
        
        final var n = 25; // 行数
        final var size = n*5; // 数据的长度需要确保是n的整数倍,应为接下来会对size金宠重排序,如何不是整数倍,reshape的时候由于采用循环补填会造成排序结果混乱。
        final var mx = NVec.of(Range.of(size)).map(LittleTree.kvp_int()).sorted((a, b) ->
            Stream.of(a, b).map(KVPair::key).map(e -> NVec.intdiv(e, n).i4(1)).reduce(Integer::compare).get() // 按照列进行排序
        ).map(KVPair::value).collect(aaclc(NVec::new)).reshape2mx(n, size/n);
        println(mx);
    }
    
    @Test
    public void sliding() {
        
        // 滑动窗口
        NVec.of(Range.P2(1, 100, 100)).slidingS(5, 5).forEach(e->{
            println(e);
        });
        
        //滑动窗口
        XVec.OF(1,2,3,4).slidingS(2, 2).forEach(e->{
            println(e);
        });
        
        //数据切片
        NVec.of(Range.of(1, 100))
        .slice(1,2,3,4,5,6,7).forEach(e->{
            println(e);
        });
    }
    
    @Test
    public void concat() {
        
        final var x = NVec.of(1,2); //[1,2]
        final var y =x.concat(x,x);
        println(y);
        final var z = NVec.of(y,y,y);
        println(z);
        println(z.slice(2)); // 再把z给切分成x
    }
    
    @Test
    public void concat2() {
        
        final var nvec = NVec.of(rG(0,100));
        println("原始里列表:\n"+nvec);
        final var xvec = nvec.splitS(10,50,99) // 生成NVec序列[d0,d1,dn],为记录简便用dd代替d0,d1序列,于是一个NVec就可以记录为[dd]
            .map(XVec::OF) // 把NVec元素包装成一个XVec，这个XVec中只有一个元素(NVec元素),相当于这样一个的的数组 [[dd]]
            .reduce(XVec::concat).get(); // 把XVec进行合并,相当于[[dd0]],[[dd1]],... 给合并成 [[dd0,dd1,...]] 的意思。
        
        println("\nsplitS结果的复合列表:");
        // xvec 已经变成了一个列表的列表，即复合列表的结构
        xvec.foreach((i,v)->{
            println(i+"-->"+v);
        });
    }
    
    @Test
    public void copy() {
        
        final var x = NVec.of(1,2); //[1,2]
        final var y =x.copyOfRange(1, 3);// 注意 to位置 超出除了x的长度
        println(y);
    }
    
    @Test
    public void cuts() {
        
        final var x = NVec.of(P2(1,10,10));// 生成一个0到10的序列
        for(int i=0;i<x.size();i++) {
            final var p = i == 0 
                ? x.cuts(0, 0) // 开始位置剪切出一个[]
                : i == x.size() - 1 
                    ? x.cuts(0, i, x.size(), x.size()) // 结束位置剪切出一个 [] 
                    : x.cuts(0, i, i + 1);
            println(p);
        }
    }
    
    @Test
    public void gets() {
        
        final var n = 10;
        final var xx = rG(0, n).mutate(NVec::new);
        println("xx",xx);
        println("xx.gets(3,5)",xx.gets(3,5));
        println("xx.gets2(3,5)",xx.gets2(3,5));
        println("xx.gets(rG(1,5))",xx.gets(rG(1,5))); 
        println("xx.gets(rG(15))",xx.gets(rG(1,15))); 
        println("xx.at(rG(15))",xx.at(rG(1,15))); 
    }

    /**
     * at 与 gets
     */
    @Test
    public void at() {
        
        println("NVec.of(1,2,3).at(0,0,3):"+NVec.of(1,2,3).at(0,0,3));
        println("NVec.of(1,2,3).gets(0,0,3):"+NVec.of(1,2,3).gets(0,0,3));
    }

    @Test
    public void range() {
        
        println("\nrg(100,200,10)");
        rg(100,200,10).forEach(e->{
            System.out.print(e+"\t");
        });
        println("\n\nRg(100,200,10)");
        Rg(100,200,10).forEach(e->{
            System.out.print(e+"\t");
        });
        println("\n\nrG(100,200,10)");
        rG(100,200,10).forEach(e->{
            System.out.print(e+"\t");
        });
        println("\n\nRG(100,200,10)");
        RG(100,200,10).forEach(e->{
            System.out.print(e+"\t");
        });
        println("\n\nNP(10, 20)");
        final var nf = NumberFormat.getInstance();
        NP(10, 20).map(nf::format).map(e->e+"\t").forEach(System.out::print);
        println("\n\nNP2(10, 20),注意结尾越过了10");
        NP2(10, 20).map(nf::format).map(e->e+"\t").forEach(System.out::print);
        println("\n");
    }
    
    @Test
    public void matrix() {
        
        final var size = 1000;
        final var matlib = new MatlibCanvas(size);
        
        println("\n--------DataMatrix-------------------------");
        final var ponits_0 = P2(-PI,PI,10).map(matlib.rhotheta_affine(1, 1, 250, 250));
        final var mx= Seq.matrix_rows(ponits_0);
        println(mx);
        println("_B 列:"+mx.cOl("_B",e->e));
        println("0 列:"+mx.coL(0, NVec::new));
        println("1 列:"+mx.coL(1, NVec::new));
        
        println("\n--------NVec.toArray2-------------------------");
        final var points_1 = P2(-PI,PI,10).map(matlib.rhotheta_affine_offset(250, 250));// 生成一组xy的点
        final var xvec = NVec.toArray2(points_1,compose_f(XVec::OF,Seq::transpose)).fmap(NVec::new);
        xvec.foreach((i,v)->{
            println(i+"-->"+v);
        });
        
        println("\n--------XVec.toArrayR-------------------------");
        final var points_2 = P2(-PI,PI,10).map(matlib.rhotheta_affine_offset(250, 250));
        final var rr = Seq.toArrayR(points_2,XVec::OF).fmap(NVec::new);
        rr.foreach((i,v)->{
            println(i+"-->"+v);
        });
        
        println("\n--------XVec.toArrayC-------------------------");
        final var points_3 = P2(-PI,PI,10).map(matlib.rhotheta_affine_offset(250, 250));
        final var cc = Seq.toArrayC(points_3,XVec::OF).fmap(NVec::new);
        cc.foreach((i,v)->{
            println(i+"-->"+v);
        });
    }
    
    /**
     * 随机变量数字特征
     */
    @Test
    public void randvar() {
        
        final var x = NVec.of(rG(0,100));
        final var stats = x.collect(Collectors.summarizingDouble(e->e));
        println("x:"+x);
        println("stats:"+stats);
        println("方差:"+x.var());
        println("sd:"+x.sd());
        println("centerize:"+x.meanN().sub2(x));
    }
    
    /**
     * xvec的形状
     */
    @Test
    public void shape() {
        
        final var nvec = rG(0,99).mutate(NVec::new); // 生成一条数据向量
        final var xN = nvec.sliceX(NVec::new, 10);// 按10的长度进行分解
        println("shape:"+xN.shape());
        final var xX = xN.fmap(n->XVec.OF(n.toArray()));
        println(xX.toString(e->e+"\n"));
    }
    
    /**
     * 数据归并
     */
    @Test
    public void merge() {
        
        final var data = NVec.of(Range.of(10)).mul(100); // 源数据
        println("源数据 x:",data);
        final var lambda_merge = PIPE(XVec.OF(NVec.of(Range.of(data.size())),data) // 为data匹配序号
            .fmap(e->e.toArray()).toArray())// 生成一个二维数组结构
            .bind(Seq::transpose).bind(Arrays::stream) // 转置并流化
            .bind(s->s.flatMap(Arrays::stream)) // 把流flat
            .bind(s->s.collect(aaclc(NVec::of))); // 管道的归并算法
        println("为"+data+" 匹配了序号 :\n"+lambda_merge.evaluate()); // 显示数据
    }
    
    /**
     * 数据过滤
     */
    public void filter() {
        
        final var x = NVec.of(100, i->i*1.0); // 生成一个100个数据的的数据向量
        println("源数据 x:",x);
        
        println(x.filter(XVec.OF(100, i->i%5==0))); // 使用boolean 标记来进行数据过滤
    }
    
    /**
     * NVec 可以看像猴皮筋一样的以拉伸与缩短，数据是具有弹性的
     */
    @Test
    public void resize() {
        
        final var x = NVec.of(10,i->i);
        println("源数据 x:",x);
        println("x.resize(20)",x.resize(20));
        println("x.resize(5)",x.resize(5));
        println("x.resize(0)",x.resize(0));
        println("x.resize(20,1d)",x.resize(20,1d));
        println("x.resize(20,(Double[])null)",x.resize(20,(Double[])null));
        println("x.resize(0).resize(20)",x.resize(0).resize(20));
        println("x.resize(0).resize(20,1d,2d,3d)",x.resize(0).resize(20,1d,2d,3d));
        println("x.resize(-1)",x.resize(-1));
        println("((XVec<Double>)x).resize(-1),虽然强制转换了但是还是调用了NVec的resize",((XVec<Double>)x).resize(-1));
        println("((XVec<Double>)x).resize(-1,1d),虽然强制转换了但是还是调用了NVec的resize",((XVec<Double>)x).resize(-1,1d));
        println("XVec.OF(1,2).resize(-1,1),注意 XVec会返回null,而 NVec 不会",XVec.OF(1,2).resize(-1,1));
        println("XVec.OF(1,2).resize(-1,1),注意 XVec会返回null,而 NVec 不会",XVec.OF(1,2).resize(-1));
    }
    
    /**
     * NVec 数据的插入
     */
    @Test
    public void insert() {
        
        final var x = NVec.of(10,i->i);
        println("源数据 x:",x);
        
        println("x.insert(5,(Double[])null)",x.insert(5,(Double[])null));
        println("x.insert(-1, 1d,2d,3d)",x.insert(-1,1d,2d,3d));
        println("x.insert(5,1d,2d,3d)",x.insert(5,1d,2d,3d));
        println("x.insert(100,1d,2d,3d)",x.insert(100,1d,2d,3d));
        println("x.insert(100,1d,2d,3d)",x.insert(100,x));
    }
    
    /**
     * NVec 的数据替换(索引标记范围替换)
     */
    @Test
    public void replace() {
        
        final var x = NVec.of(10,i->i);
        println("源数据 x:",x);
        
        println("x.replace(2, 5, 12d,13d,14d)",x.replace(2, 5, 12d,13d,14d));
        println("x.replace(2, null, 12d,13d,14d) 等同于在2号位置插入",x.replace(2, null, 12d,13d,14d)); // 等同于插入
        println("x.replace(2, 5, 12d,13d,14d,15d,16d)",x.replace(2, 5, 12d,13d,14d,15d,16d));
        println("x.replace(2, 2) 等于与 duplicate",x.replace(2, 2));// 等于与 duplicate
        println("x.replace(2, 5) 等同于删除",x.replace(2, 5));// 等同于删除
        println("x.replace(2, 100) 等同于删除，2号位置以后的数据",x.replace(2, 100));// 等同于删除
        println("x.replace(2, 5,x)",x.replace(2, 5,x));// 等同于删除
    }

    /**
     * NVec 数据删除
     */
    @Test
    public void delete() {
        
        final var x = NVec.of(10,i->i);
        println("源数据 x:",x);
        
        println("x.delete(0, 10)",x.delete(0, 10));
        println("x.delete(0, 8)",x.delete(0, 8));
        println("x.delete(2, 5)",x.delete(2, 5));
        println("x.delete(100, 5)",x.delete(100, 5));
        println("x.delete(-1, 5)",x.delete(-1, 5));
        println("x.delete(100, 5000)",x.delete(100, 5000));
        println("x.delete(1, 5000)",x.delete(1, 5000));
    }
    
    /**
     * 重排的shuffle演示
     */
    @Test
    public void shuffle() {
        
        final var letters = XVec.OF("赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻柏水窦章".split(""));// 基础字符集合（源数据字符序列，取自百家姓）
        final Supplier<String> namegen = ()->letters.shuffle().head(3).collect(Collectors.joining());// 随机生成3个字符长度的名字
        
        println(namegen.get());
        println("letters",letters);
        println("letters.shuffle(e->-1d*System.nanoTime())) ,逆序的一种实现",letters.shuffle(e->-1d*System.nanoTime()));
        println("letters",letters.sorted(e->Math.random()));
        println("letters.shuffle()",letters.shuffle());
        println("letters.shuffle().gets(rG(0,10))",letters.shuffle().gets(rG(0,10)));
        println("letters.shuffle().head(100)",letters.shuffle().head(100));
        println("letters.shuffle().head(100)",letters.shuffle().bighead());
        println("letters.shuffle().head(100)",letters.shuffle().bigtail());
        println("nvec shuffle",NVec.of(10,i->i).shuffle());
        println("nvec Weightable.wtof",NVec.of(10,i->i).stream(Weightable.weightf(e->Math.random())).sorted().collect(IRecord.llclc(e->e)));
        println("nvec Weightable.wtof",NVec.of(10,i->i).shuffle(e->-e));// 逆顺序
        println("nvec sorted",NVec.of(10,i->Math.random()).sorted());
        println("列序矩阵:\n",NVec.of(16,i->i+1).shuffle2((i,d)->i%4).reshape(4,4));
        println("行顺序矩阵:\n",NVec.of(16,i->i+1).shuffle2((i,d)->1).reshape(4,4));
    }
    
    /**
     * iterator 的演示
     */
    @Test
    public void iterator() {
        
        final var y = NVec.of(10,i->i+1);
        for(var p:y) {
            System.out.println(p);
        }
        System.out.println("-----------------------");
        final var itr = y.iterator(); // 迭代器
        while(itr.hasNext()) {
            System.out.println(itr.next());
        }
    }
    
    /**
     * 数据分片
     */
    @Test
    public void slice() {
        
        final var x = NVec.of(1000,i->i).collect(PVec.pvec1clc(i->i%5));
        final var rdd = x.fmap1(Object::toString).sliceS(e->e,100).map(PVec::new).collect(XVec.xveclc()); // 分片
        final var xvec = rdd.fmap(p->p.reduceByKey(Collectors.summarizingDouble(e->e)));
        xvec.foreach((i,p)->{
            System.out.println(i+"---->"+p);
        });
    }
    
    /**
     * 集合计算
     */
    @Test
    public void set_ops(){
        
        println("union",NVec.of(10,i->i).union(NVec.of(20,i->i)));
        println("intersect",NVec.of(10,i->i).intersect(NVec.of(5,i->i)));
        println("diff",NVec.of(10,i->i).diff(NVec.of(5,i->i)));
        println("contains",NVec.of(10,i->i).contains(50d));
    }
    
    /**
     * 数据拉链
     */
    @Test
    public void zip() {
        
        final var xx = NVec.of(10,i->i*10);
        println(xx.collect(PVec.zipclc(NVec.of(1,2,3))));
    }
    
    /**
     * 把连续类型的变量转换成分类变量
     */
    @Test
    public void discrete() {
        final var data = NVec.of(10, i -> Math.random()); // 源数据
        final var percentiles = withTU(data.sorted(), NVec.of(0, 25, 50, 75, 90).mul(data.size() / 100d),
                (a, n) -> a.gets(n)); // 百分位数
        println("区间范围", percentiles.prepend(0d).bighead().zip(percentiles)); // 生成区间范围
        final var x = percentiles.zip(XVec.OF("极小,较小,小,大,较大".split(","))); // 分类变量
        println("分类属性", x);
        PatternCase.nullcase(100).lte(100, 100);
        final var fn = withT(defaultcase((Double) null, "非法值"), p -> x.collect(Collector.of(() -> p, 
                (pcase, b) -> pcase.lte(b._1(), b._2()), PatternCase::addAll))).fn(); // 符号化函数
        println("离散/分类变量 未排序", data.fmap(fn), "\n - ", data);
        println("离散/分类变量 排序", data.sorted().fmap(fn), "\n - ", data.sorted());
    }
    
    /**
     * 数据累积求和
     */
    @Test
    public void accum() {
        println("数据求阶乘", NVec.of(10, i -> i + 1).accumN2(e -> e.product())); // 数据求阶乘
        println("数据求累加", NVec.of(10,i->i+1).accum2()); // 数据求累加
        // 切分演示
        println("seq 阐述索引位置序列", NVec.of(10, i -> i + 1).splitX(NVec.of(5, 2).accum2())); // seq 阐述索引位置序列
        println("数组阐述索引位置序列", NVec.of(10, i -> i + 1).splitX(NVec.of(5, 2).accum2().intA())); // seq 阐述索引位置序列
    }
    
    /**
     * 数据切分
     */
    @Test
    public void splitX() {
        NVec.of(100,i->i+1).splitX(NVec.of(20,i->i+1).accum2()).foreach(e->println(e));
    }
    
    final String[] ss = "0123456789".split("");
    final String[][] ranges = new String[][] { ss, ss, ss, ss, ss, ss };
}
